Access Function세터(setter), 게터(getter)
위의 코드는 연산처럼 보이지 않으며, 읽기도 쉽지 않다
레퍼런스를 반환하는 멤버 함수를 작성하는 것이 더 좋다
class complex{
public:
double& real(){ return r; }
private:
double r=0;
double i=0;
};
int main(void){
complex c;
c.real()+=5.;
inline double& real(complex& c){ return c.real(); }
real(c)+=5;
}
자유함수에서 private 변수로 접근할 수 있도록, friend로 선언해 줄 수 있다.
class complex{
friend double& real(complex& c);
};
complex가 상수일 때, 접근하기 위한 함수(함수 const 버전)
inline const double& complex(const complex& c){ return c.r; }
스코프
real(c)+=5;
double& rr=real(c);
c와 rr을 동일한 스코프에서 정의한 경우에도 C++에서 개체 선언의 역순으로 파괴를 수행하기 때문에
c가 rr보다 오래 생존한다.
임시 개체의 멤버 참조는 동일한 표현식 내에서 안전하게 사용할 수 있다.
double r2=real(complex(3.7))*2.0;
임시 complex: complex(3.7)은 해당 문장(statement)에서만 생존한다.
하지만, 실수부의 레퍼런스보다는 길게 살 수 있기 때문에 문제가 없다.
const double& rr=real(complex(3, 7));
cout<<"The real part is "<<rr<<'\n';
하지만 위의 코드에서 rr은 쓰레기값을 가르키는 레퍼런스이다.
complex(3, 7)은 임시 complex로 이 때 반환한 레퍼런스는 첫번째 문장이 끝날 때까지만 생존한다.
임시 표현식의 레퍼런스를 유지하지 말것
첨자 연산자
class vector{
public:
double at(int i){
assert(i>=0 && i<my_size);
return data[i];
}
private:
int my_size;
double* data;
};
int main(void){
double sum=0.0;
for(int i=0; i<v.size(); ++i){
sum+=v.at(i);
}
}
위의 코드에서 만일 첨자 연산자를 사용할 수 있다면, 아래와 같이 코드를 사용할 수 있다.
class vector{
public:
double operator[](int i){
assert(i>=0 && i<my_size);
return data[i];
}
private:
int my_size;
double* data;
};
int main(void){
double sum=0.0;
for(int i=0; i<v.size(); ++i){
sum+=v[i];
}
}
하지만 위의 코드는 const 벡터의 값을 변경할 수는 없다.
상수 멤버 함수(const member function)상수 개체를 받는 연산자와 멤버함수를 작성하기 위해서는 상수 멤버함수를 선언해 주어야 한다.
class vector{
public:
const double& operator[](int i) const{
assert(i>=0 && i<=my_size);
return data[i];
}
};
상수 멤버 함수의 const는 단순히 프로그래머가 상수 개체로 멤버 함수를 호출하는 것을 꺼리지 않는다는
의사 표시가 아니다.
C++ 컴파일러는 불변성을 심각하게 받아들이고, 함수가 개체를 수정하지 않고 개체를 다른 함수에 const 인수로만
전달하는지 확인한다.(다른 메서드를 호출할 때, const 이어야 함)
불변성 보증은 데이터 멤버가 가르키는 상수가 아닌 포인터나 레퍼런스를 반환하는 것을 막음
반환개체는 현재 개체, 멤버 변수(또는 상수) 중 하나 또는 임시 변수 중 하나의 복사본이어야 함
상수 멤버함수는 상수가 아닌 개체를 인수로 호출할 수 있다.
(C++ 내부에서 암시적으로 상수가 아닌 레퍼런스를 상수 레퍼런스로 변환하여 사용함)
class vector{
public:
int size() const { return my_size; }
};
const 멤버 함수가 아닌 size() 함수는 const 멤버 함수 size() 함수와 동일하게 동작
하지만 첨자 연산자의 경우, const인 버전과 const 아닌 버전이 별도로 필요함
(const 버전은 벡터의 요소를 수정할 수 없음)
데이터 멤버를 mutable로 사용할 경우, 값을 변경할 수 있음.
mutable은 캐시와 같이 관찰 가능한 행동에 영향을 주지 않는 내부 상태를 위한 기능임
레퍼런스로 한정된 멤버(Lvalue로 한정)C++11에서는 개체의 불변성(*this) 이외에도 개체가 Lvalue 또는 Rvalue 레퍼런스이어야 한다고 요구할 수 있다.
(v+w)[i]는 변경 가능한 Lvalue이다.
첨자 연산자를 변경가능한 첨자 연산자와 const 첨자 연산자를 오버로드해주었을 경우,
v+w는 변경 가능한 벡터를 받는 첨자 연산자를 사용한다.
(변경 가능한 개체의 멤버를 참조하는 변경 가능한 레퍼런스에 접근한다.)
이 때, (v+w)[i]는 Lvalue가 되고 v+w는 Lvalue가 아니다.
Lvalue에만 적용될 수 있는 첨자 연산자 정의
class vector{
public:
double& operator[](int i) & { ... }
double& operator[](int i) const& { ... }
};
int main(void){
(v+w)[i]=3;
}
위 코드로 생성된 vector는 임시 레퍼런스(Rvalue)에 대하여 첨자 연산자를 사용할 수 없다.
동일하게 임시 개체를 비활성화하기 위해 벡터의 할당 연산자를 레퍼런스로 한정(Ref-Qualify) 할 수 있다.
(할당 연산자 우측의 변수를 Rvalue로 제한)
&를 두 개 사용함으로써 멤버 함수를 Rvalue로 제한할 수 있다.
class vector{
public:
double& operator=(vector other) && { ... }
const double& operator=(vector other) const&& { ... }
};